﻿namespace Blaze.Framework.Loaders
{
    using System;
    using System.Collections;
    using Logging;
    using UnityEngine;

    /// <summary>
    /// 用于加载任意资源。
    /// </summary>
    public sealed class AssetLoader : AssetLoaderBase
    {
        /// <summary>
        /// 获取需要加载的资源所在的资源包名。
        /// </summary>
        public string AssetBundleName { get; private set; }

        /// <summary>
        /// 获取需要加载的资源名。
        /// </summary>
        public string AssetName { get; private set; }

        /// <summary>
        /// 获取需要加载的资源类型。
        /// </summary>
        public Type AssetType { get; private set; }

        /// <summary>
        /// 获取加载进度。
        /// </summary>
        public override float Progress
        {
            get { return (mAssetBundleLoadingProgress + mAssetLoadingProgress) / 2; }
        }

        /// <summary>
        /// 当加载器需要被释放时调用此方法。
        /// </summary>
        protected override void OnDispose()
        {
            if (mAssetBundleLoader != null)
                AssetLoaderPool.Put(mAssetBundleLoader);
            base.OnDispose();
        }

        protected override void OnStart()
        {
            var array = Uri.Split('&');
            AssetBundleName = array[0];
            AssetName = array[1];
            if (array.Length > 2)
            {
                AssetType = Type.GetType(array[2]);
                if (AssetType == null)
                    mLog.Warn("[AssetLoader]Invalid asset type -> " + array[2]);
            }
            else
            {
                AssetType = typeof(UnityEngine.Object);
            }

#if UNITY_EDITOR
            var appConfig = AppConfigManager.Get<AppConfig>();
            if (appConfig.simulate_runtime)
                CoroutineManager.StartNew(loadFromAssetBundle());
            else
                CoroutineManager.StartNew(loadFromEditor());
#else
            CoroutineManager.StartNew(loadFromAssetBundle());
#endif
        }

        private void finish(UnityEngine.Object asset)
        {
#if UNITY_EDITOR
            fixShaderReferences(asset);
#endif
            Finish(null, asset);
        }

        private IEnumerator loadFromAssetBundle()
        {
            mAssetBundleLoader = AssetLoaderPool.Get<AssetBundleLoader>(AssetBundleName)
                .SetMode(Mode)
                .FluentStart();
            while (!mAssetBundleLoader.IsDone)
            {
                mAssetBundleLoadingProgress = mAssetBundleLoader.Progress;
                yield return null;
            }

            if (mAssetBundleLoader.Error != null)
            {
                Finish(mAssetBundleLoader.Error, null);
                yield break;
            }

            if (mAssetBundleLoader.AssetBundle == null)
            {
                Finish(Uri + " assetbundle not exists", null);
                yield break;
            }

            mAssetBundleLoadingProgress = 1;

            switch (Mode)
            {
                case LoadAssetMode.Sync:
                    var asset = mAssetBundleLoader.AssetBundle.LoadAsset(AssetName, AssetType);
                    finish(asset);
                    break;
                case LoadAssetMode.Async:
                    var request = mAssetBundleLoader.AssetBundle.LoadAssetAsync(AssetName, AssetType);
                    while (!request.isDone)
                    {
                        mAssetLoadingProgress = request.progress;
                        yield return null;
                    }
                    mAssetLoadingProgress = 1;
                    finish(request.asset);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private static readonly Log mLog = LogManager.GetLogger(typeof(AssetLoader));
        private AssetBundleLoader mAssetBundleLoader;
        private float mAssetBundleLoadingProgress;
        private float mAssetLoadingProgress;

#if UNITY_EDITOR

        private IEnumerator loadFromEditor()
        {
            var assetPaths = UnityEditor.AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(AssetBundleName, AssetName);
            for (var index = 0; index < assetPaths.Length; index++)
            {
                var assetPath = assetPaths[index];
                var result = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, AssetType);
                if (result != null)
                {
                    Finish(null, result);
                    yield break;
                }
            }
            Finish($"从编辑器加载资源失败 assetbundle={AssetBundleName} asset={AssetName}", null);
        }

        private static void fixShaderReferences(UnityEngine.Object asset)
        {
            var gameObject = asset as GameObject;
            if (gameObject == null)
                return;

            var renderers = gameObject.GetComponentsInChildren<Renderer>();
            foreach (var renderer in renderers)
            {
                if (renderer.sharedMaterials == null)
                    continue;
                foreach (var material in renderer.sharedMaterials)
                {
                    if (material == null || material.shader == null)
                        continue;

                    material.shader = Shader.Find(material.shader.name);
                }
            }
        }
#endif
    }
}